#include "copycat.h"

//////////////////////////////////////////////////////////////////////////////
CCopycat::CCopycat()
{
	num_sent = 0;
}


//////////////////////////////////////////////////////////////////////////////
CCopycat::~CCopycat()
{
}

//////////////////////////////////////////////////////////////////////////////
bool CCopycat::LoadCorpus( char *parse_fn, char *giza_fn, char *align_fn )	
{
	CUTextFile parse_file( parse_fn, "r" );
	if( parse_file.IsOpen() == false )	{
		fprintf( stderr, "ERROR: file open fail %s\n", parse_fn );
		return false;	
	}

	fprintf( stderr, "[Loading Word Align ]   " );

	CGiza giza;
	if( giza.Open( giza_fn ) == false )	return false;

	int num_line = parse_file.GetNumLine(); parse_file.MoveToStart();
	sent = new CSent[num_line+10];
	num_sent = 0;

	while(1)	{
		CUString line;
		if( parse_file.ReadLine( line ) == false )	break;
		line.Replace("\r", "" );
		
//		if( num_sent > 100 )	break;
//		if( now_line % 1000 == 0 )		fprintf( stderr, "." );
//		if( num_sent % 10000 == 0 )		fprintf( stderr, "." );

		if( line.GetLength() == 0 )	{
			fprintf( stdout, "ERROR: null line %d\n", num_sent );	return false;
		}

/*
		if( line.Find("(ROOT (") == 0 )	{
			line = line.SubStr( 6, line.GetLength()-1 );
		}
		*/


		// load parse string
		CSent &s = sent[num_sent];
		s.parse = line;
		s.id = num_sent;

		// load word alignment
		if( giza.ReadSent() == false )	{
			fprintf( stderr, "Fail to read giza\n" );
			return false;
		}
		s.src_tok_list.Clear();
		for( int i=1; i<giza.src.tok_list.GetSize(); i++ )	{
			s.src_tok_list.PushBack( giza.src.tok_list.GetAt(i) );
		}

		s.align_list.Clear();
		for( int i=1; i<giza.align.align_list.GetSize(); i++ )	{
			CUString str = giza.align.align_list.GetAt(i);
			int align = -1;
			if( str != "" )	align = atoi( str.GetStr() )-1;
			char tmp[16]; sprintf( tmp, "%d", align );
			s.align_list.PushBack( tmp );
		}

		s.tgt_tok_list.Clear();
		for( int i=0; i<giza.tgt.tok_list.GetSize(); i++ )	{
			s.tgt_tok_list.PushBack( giza.tgt.tok_list.GetAt(i) );
		}
		num_sent++;
	}
	fprintf( stderr, "%d sentences\n", num_sent );

	return true;
}


//////////////////////////////////////////////////////////////////////////////
bool CCopycat::LoadCorpus( char *parse_fn, char *gold_giza_fn )
{
	CUTextFile parse_file( parse_fn, "r" );
	if( parse_file.IsOpen() == false )	{
		fprintf( stderr, "ERROR: file open fail %s\n", parse_fn );
		return false;	
	}

	fprintf( stderr, "[Loading Answer Word Align ]   " );

	CGiza giza;
	if( giza.Open( gold_giza_fn ) == false )	return false;

	int num_line = parse_file.GetNumLine(); parse_file.MoveToStart();
	sent = new CSent[num_line+10];
	num_sent = 0;

	while(1)	{
		CUString line;
		if( parse_file.ReadLine( line ) == false )	break;
		line.Replace("\r", "" );
		

		if( line.GetLength() == 0 )	{
			fprintf( stdout, "ERROR: null line %d\n", num_sent );	return false;
		}


		// load parse string
		CSent &s = sent[num_sent];
		s.parse = line;
		s.id = num_sent;

		// load word alignment
		if( giza.ReadSent() == false )	{
			fprintf( stderr, "Fail to read giza\n" );
			return false;
		}
		s.src_tok_list.Clear();
		for( int i=1; i<giza.src.tok_list.GetSize(); i++ )	{
			s.src_tok_list.PushBack( giza.src.tok_list.GetAt(i) );
		}

		s.align_list.Clear();
		for( int i=1; i<giza.align.align_list.GetSize(); i++ )	{
			CUString str = giza.align.align_list.GetAt(i);
			int align = -1;
			if( str != "" )	align = atoi( str.GetStr() )-1;
			char tmp[16]; sprintf( tmp, "%d", align );
			s.align_list.PushBack( tmp );
		}

		s.tgt_tok_list.Clear();
		for( int i=0; i<giza.tgt.tok_list.GetSize(); i++ )	{
			s.tgt_tok_list.PushBack( giza.tgt.tok_list.GetAt(i) );
		}
		num_sent++;
	}
	fprintf( stderr, "%d sentences\n", num_sent );

	return true;
}




//////////////////////////////////////////////////////////////////////////////
CUString CCopycat::GetRuleStr( CTree &t, CNode &n )
{
	CUString str = n.label;
	for( int i=0; i<n.num_child; i++ )	{
		int cid = n.child[i];
		str += " ["; str += t.node[cid].label; str +="]";
	}
	return str;
}


//////////////////////////////////////////////////////////////////////////////
int CCopycat::GetNumCross( CTree &t, CNode &n, CSent &s )
{
	int ls[NUM_MAX_NODE];
	int num=-1;

//	fprintf( stdout, "%s\n", s.parse.GetStr() );
//	fprintf( stdout, "%s\n", n.label.GetStr() );
	t.GetLexNodeList( ls, num, n.id );

	int num_cross = 0;
	int num_align = 0;

	CUStringList align_pair_list;
	for( int i=0; i<num;i++ )	{
		int id = ls[i];
		CNode &lex_node = t.node[id];
		for( int j=0; j<lex_node.num_tok_aligns; j++ )	{
			int a = lex_node.tok_aligns_tgt[j];
			char buf[1024];
			sprintf( buf, "%d-%d", i, a );
			align_pair_list.PushBack( buf );
		}
	}

	for( int i=0; i<align_pair_list.GetSize(); i++ )	{
		CUString str= align_pair_list.GetAt(i);
		CUStringList t; t.TokenizeByChar( str, "-", false );
		int src_id = atoi(t.GetAt(0) );
		int tgt_id = atoi(t.GetAt(1) );
		num_align++;

		for( int j=0; j<align_pair_list.GetSize(); j++ )	{
			if( i == j )	continue;

			bool have_cross = false;
			CUString str2 = align_pair_list.GetAt(j);
			CUStringList t2; t2.TokenizeByChar( str2, "-", false );
			int src_id2 = atoi(t2.GetAt(0) );
			int tgt_id2 = atoi(t2.GetAt(1) );


			if( src_id < src_id2 && tgt_id > tgt_id2 )	{
				have_cross = true;
			}
			if( src_id > src_id2 && tgt_id < tgt_id2 )	{
				have_cross = true;
			}

			if( have_cross == true )	{
				//fprintf( stdout, " !cross %d - %d\n", src_id, tgt_id );
				num_cross++;
				break;
			}

		}
	}

//	align_pair_list.PrintEx( stdout, " " ); fprintf( stdout, "\n" );

	return num_cross;
}


double CCopycat::GetAvgAlign( CTree &t, int start )
{
	int ls[NUM_MAX_NODE];
	int m=-1;
	t.GetLexNodeList( ls, m, start );

	int sum=0;
	int num_align=0;
	for( int k=0; k<m; k++ )	{
		CNode &l = t.node[ ls[k] ];
		for( int p=0; p<l.num_tok_aligns; p++ )	{
			num_align++;
			sum += l.tok_aligns_tgt[p];
		}
	}

	if( num_align == 0 )	return -1;
	return (double)sum / (double)num_align;
}


bool CCopycat::TreeInspect( char *parse_fn, char *giza_fn, char *align_fn, char *out_fn )
{
	fprintf( stderr, "Start to inspect\n" );
	if( LoadCorpus( parse_fn, giza_fn, align_fn ) == false )	return false;

	CUTextFile file( out_fn, "w" );
	if( file.IsOpen() == false )	return false;

	int num_find=0;
	for( int i=0; i<num_sent; i++ )	{
		fprintf( stderr, "\r[Inspect]   %d / %d sentences processed (# Subtree: %d)", i, num_sent, num_find );

		CSent &s = sent[i];

		// load parse tree
		CTree t ;
		t.Init();
		if( t.LoadTree( s.parse ) == false )	return false;
		if( SetNodeSpan( s, t ) == false )	{
			fprintf( stderr, "error %d\n", i );
			return false;
		}

		// for each node
		for( int j=0; j<t.num_node; j++ )	{
			CNode &n = t.node[j];

			if( n.num_child == 2 || n.num_child == 3 )	{
			}
			else	{
				continue;
			}
			if( IsAllChildrenAligned( t, n ) == false )	continue;

			CUString rule_str = GetRuleStr( t, n );
			if( rule_str == "ADJP [JJ] [NP]" )	{
				num_find++;
				fprintf( file.GetFP(), "sent %d\n", i );

				n.label = CUString("###" ) + n.label + CUString("###" );
				CUString tmp; t.PrintStrRecurr( 0, tmp );
				fprintf( file.GetFP(), "%s\n", tmp.GetStr() );
				double avg = GetAvgAlign( t, n.id );
				fprintf( file.GetFP(), "%s (%d~%d) avg:%1.2f\n", n.label.GetStr(), n.span_from, n.span_to, avg );
				for( int k=0; k<n.num_child; k++ )	{
					CNode &c = t.node[ n.child[k] ];
					double avg_c = GetAvgAlign( t, c.id );
					fprintf( file.GetFP(), "   %s (%d~%d) %1.2f\n", c.label.GetStr(), c.span_from, c.span_to, avg_c );
				}
				fprintf( file.GetFP(), "\n" );


			}

		}

	}
	fprintf( stderr, "\r[Inspect]   %d / %d sentences processed (# Subtree: %d)\n", num_sent, num_sent, num_find );


	return true;
}

CUString CCopycat::GetRuleAfterStr( CTree &t, CNode &n )
{
	// get avg
	double avg_c[1024];
	int idx[1024];
	for( int i=0; i<n.num_child; i++ )	{
		avg_c[i] = GetAvgAlign( t, n.child[i] );
		idx[i] = i;
	}



	// sort
	for( int i=0; i<n.num_child; i++ )	{
		double var1 = avg_c[ i ];
		for( int j=i+1; j<n.num_child; j++ )	{
			double var2 = avg_c[ j ];

			if( var1 > var2 )	{
				int tmp = idx[i];
				idx[i] = idx[j];
				idx[j] = tmp;

				double tmp2 = avg_c[i];
				avg_c[i] = avg_c[j];
				avg_c[j] = tmp2;
			}

		}
	}

	// to string
	CUString str;
	for( int i=0; i<n.num_child; i++ )	{
		char buf[1024];
		if( i > 0 )	{
			str += " ";
		}
		CNode &c_node = t.node[ n.child[ idx[i] ] ];
		sprintf( buf, "A%d", idx[i] );
		str += buf;
	}

	return str;
}


bool CCopycat::RuleEval( char *parse_fn, char *answer_nao_fn,  char *rule_fn,  char *out_fn )
{
	fprintf( stderr, "Start to Rule Eval\n" );
	if( LoadCorpus( parse_fn, answer_nao_fn ) == false )	return false;


	CRuleRF rule_rf;
	rule_rf.Clear();
	if( rule_rf.LoadRule( rule_fn, 0, 0.0 ) == false )	{
		return false;
	}

	int eval_correct_cnt = 0;
	int eval_apply_cnt = 0;
	int eval_node_cnt = 0;


	int num_find=0;
	for( int i=0; i<num_sent; i++ )	{
		fprintf( stderr, "\r[Rule Eval]   %d / %d sentences processed", i, num_sent );

		CSent &s = sent[i];

		// load parse tree
		CTree t ;
		t.Init();
		if( t.LoadTree( s.parse ) == false )	return false;
		if( SetNodeSpan( s, t ) == false )	{
			fprintf( stderr, "error %d\n", i );
			return false;
		}

		// for each node
		eval_node_cnt += t.num_node;
		for( int j=0; j<t.num_node; j++ )	{
			CNode &n = t.node[j];

			if( n.num_child < 2 )	continue;
			if( IsAllChildrenAligned( t, n ) == false )	continue;


			CUString rule_str = GetRuleStr( t, n );

			/*
				if( rule_str == "VP [VB] [VP]" )	{
					fprintf( stdout, "%s\n", s.parse.GetStr() );
					s.src_tok_list.PrintEx( stdout );
					s.tgt_tok_list.PrintEx( stdout );
					s.align_list.PrintEx( stdout );
				}
				*/

			map<string, CRuleRF>::iterator itr = rule_rf.rule_map.begin();
			while( itr != rule_rf.rule_map.end() )	{
				CUString before = itr->first.c_str();
				before = before.Left( before.Find("->") );
	//			fprintf( stdout, "%s\t%s\n", rule_str.GetStr(), before.GetStr() );
				if(  rule_str == before )	{
					(itr->second.eval_apply_cnt)++;
					eval_apply_cnt++;



					CUString ans_after = GetRuleAfterStr( t, n );
					CUString rule_after = itr->second.after;
					if( rule_after == ans_after ) {
						(itr->second.eval_correct_cnt)++;
						eval_correct_cnt++;
					}
					break;
				}


				itr++;
			}

		}

	}
	fprintf( stderr, "\r[Rule Eval]   %d / %d sentences processed\n", num_sent, num_sent );
	fprintf( stderr, "precion %1.4f ( %d / %d )\n", eval_correct_cnt, eval_apply_cnt, (double)eval_correct_cnt/(double)eval_apply_cnt );
	fprintf( stderr, "coverag %1.4f ( %d / %d )\n", eval_apply_cnt, eval_node_cnt, (double)eval_apply_cnt/(double)eval_node_cnt );

	CUTextFile file( out_fn, "w" );
	if( file.IsOpen() == false )	return false;
	rule_rf.SaveToEvalFile( file.GetFP() );


	return true;
}
bool CCopycat::IsAllChildrenAligned( CTree &t, CNode &n )
{
	for( int i=0; i<n.num_child; i++ )	{
		CNode &c = t.node[ n.child[i] ];
		if( c.span_from == NULL_NUM ) return false;
		if( c.span_to   == NULL_NUM ) return false;
	}

	return true;
}


//////////////////////////////////////////////////////////////////////////////
// EOF



